home *** CD-ROM | disk | FTP | other *** search
/ 10,000 Great Games / 10,000 Great Games.iso / Product / 66 / data1.cab / Source_Files / Src / Surface.cpp < prev    next >
C/C++ Source or Header  |  2000-01-16  |  8KB  |  373 lines

  1. #include "stdafx.h"
  2.  
  3. cSurface::cSurface(int _x, int _y, int _w, int _h, int _total_h, int _edge, int _transparent, int _usescreen, int _dirty_block)
  4. {    
  5.     // Dimensions
  6.     
  7.     screen_x = _x;
  8.     screen_y = _y;
  9.     w = _w;
  10.     h = _h;
  11.  
  12.     // Parameters 
  13.  
  14.     total_h = _total_h;
  15.     edge = _edge;
  16.     transparent = _transparent;    
  17.     usescreen = _usescreen && !no_blit_hardware && hardware_blit_caps;
  18.  
  19.     // Dirty
  20.  
  21.     dirty_block = _dirty_block;
  22.     dirty_w = w / dirty_block;
  23.     dirty_h = h / dirty_block;
  24.     dirty = new char [dirty_w * dirty_h];
  25.  
  26.     ASSERT(w % dirty_block == 0 && h % dirty_block == 0);
  27.  
  28.     // Reset start and dirty
  29.  
  30.     reset();
  31.  
  32.     // Make surface 
  33.     
  34.     if (!usescreen)
  35.     {
  36.         // A separate buffer is used which must be blit into the backbuffer 
  37.  
  38.         offset_x = 0, offset_y = 0;
  39.  
  40.         dds = create_surface(w, h, no_blit_hardware || !hardware_blit_caps? DDSCAPS_SYSTEMMEMORY:0, transparent? DDSD_CKSRCBLT:0);        
  41.         
  42.         if (dds == 0)
  43.             error("Unable to create game surface");
  44.  
  45.         scroll_src_dds = dds;
  46.     }
  47.     else
  48.     {
  49.         // The backbuffer of the screen is directly used, 
  50.         // which is faster but not possible for parallax scrolling         
  51.  
  52.         offset_x = screen_x, offset_y = screen_y;
  53.  
  54.         dds = backbuffer;
  55.  
  56.         scroll_src_dds = inawin? backbuffer:screen;
  57.     }
  58.  
  59.     // Get caps 
  60.  
  61.     if (FAILED(dds->GetCaps(&caps)))
  62.         error("Unable to get capabilities for surface");
  63. }    
  64.  
  65. cSurface::~cSurface()
  66. {
  67.     if (!usescreen)
  68.         dds->Release();
  69.  
  70.     safe_delete(&dirty);
  71. }
  72.  
  73. void cSurface::clear_backbuffer(int color)
  74. {
  75.     // Setup effect for blit
  76.  
  77.     DDBLTFX ddbltfx;
  78.     
  79.     ddbltfx.dwSize = sizeof(ddbltfx);
  80.     ddbltfx.dwFillColor = color;
  81.  
  82.     // Do clear
  83.     
  84.     CRect r(screen_x, screen_y, screen_x + w, screen_y + h);
  85.     while (!draw_ok(backbuffer->Blt(&r, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &ddbltfx)));
  86. }
  87.  
  88. void cSurface::blit_to_backbuffer()
  89. {
  90.     if (!usescreen)
  91.     {
  92.         // Setup rectangle for blit 
  93.         
  94.         CRect r(0, 0, w, h);
  95.     
  96.         // Do blit 
  97.     
  98.         while (!draw_ok(backbuffer->BltFast(screen_x, screen_y, dds, &r, DDBLTFAST_WAIT | (transparent? DDBLTFAST_SRCCOLORKEY:DDBLTFAST_NOCOLORKEY))));
  99.     }
  100. }
  101.  
  102. int cSurface::convert_area(int &x1, int &y1, int &x2, int &y2)
  103. {
  104.     // Convert to surface coordinates
  105.  
  106.     y1 = start + h - 1 - y1;
  107.     y2 = start + h - 1 - y2;
  108.  
  109.     // Clip
  110.  
  111.     limit_lowerbound(x1, 0);
  112.     limit_upperbound(x2, w - 1);
  113.     limit_lowerbound(y1, 0);
  114.     limit_upperbound(y2, h - 1);
  115.  
  116.     // Check if anything is on screen
  117.  
  118.     if (x1 > x2 || y1 > y2)
  119.         return FALSE;
  120.  
  121.     // Compute results
  122.  
  123.     x1 /= dirty_block;
  124.     y1 /= dirty_block;    
  125.     x2 /= dirty_block;
  126.     y2 /= dirty_block;    
  127.  
  128.     // Return computation succeeded
  129.  
  130.     return TRUE;
  131. }
  132.  
  133. void cSurface::reset()
  134. {
  135.     // Reset start
  136.  
  137.     last_start = 0;
  138.     start = 0;
  139.     
  140.     // Reset dirty
  141.  
  142.     reset_dirty();
  143. }
  144.  
  145. void cSurface::reset_dirty()
  146. {
  147.     // Make everything not dirty
  148.  
  149.     FillMemory(dirty, dirty_w * dirty_h, FALSE);
  150. }
  151.  
  152. void cSurface::all_dirty() 
  153.     // Make everything dirty
  154.  
  155.     FillMemory(dirty, dirty_w * dirty_h, TRUE);
  156. }
  157.  
  158. void cSurface::add_dirty(int x1, int y1, int x2, int y2)
  159. {
  160.     // Get area to set
  161.  
  162.     if (!convert_area(x1, y1, x2, y2))
  163.         return;
  164.  
  165.     // Set area dirty
  166.  
  167.     char *d = dirty + x1 + y1 * dirty_w;
  168.     
  169.     for (; y1 <= y2; y1++, d += dirty_w)
  170.         FillMemory(d, x2 - x1 + 1, TRUE);
  171. }
  172.  
  173. void cSurface::all_surfaces_dirty()
  174. {
  175.     game_surface->all_dirty();
  176.  
  177.     if (!no_parallax)
  178.         back_surface->all_dirty();
  179.     
  180.     left_surface->all_dirty();
  181.     right_surface->all_dirty();
  182. }
  183.  
  184. void cSurface::all_surfaces_not_dirty()
  185. {
  186.     game_surface->reset_dirty();
  187.  
  188.     if (!no_parallax)
  189.         back_surface->reset_dirty();
  190.     
  191.     left_surface->reset_dirty();
  192.     right_surface->reset_dirty();
  193. }
  194.  
  195. void cSurface::do_scroll()
  196.     // Get scroll difference 
  197.     
  198.     int delta = start - last_start;
  199.     
  200.     // Remember new start
  201.  
  202.     last_start = start;
  203.  
  204.     // Scroll difference up or down
  205.     
  206.     if (delta > 0)
  207.     {
  208.         // Scroll up
  209.  
  210.         if (delta < h)
  211.         {
  212.             // Scroll
  213.             
  214.             CRect r(offset_x, offset_y, offset_x + w, offset_y + h - delta);
  215.             while (!draw_ok(dds->BltFast(offset_x, offset_y + delta, scroll_src_dds, &r, DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT)));        
  216.  
  217.             // Make area dirty
  218.  
  219.             add_dirty(0, start + h - 1, w - 1, start + h - delta);
  220.         }
  221.         else
  222.         {
  223.             // Everything is dirty
  224.  
  225.             all_dirty();
  226.         }
  227.     }
  228.     else if (delta < 0)
  229.     {
  230.         // Scroll down
  231.  
  232.         if (delta > -h)
  233.         {
  234.             // Scroll
  235.         
  236.             CRect r(offset_x, offset_y - delta, offset_x + w, offset_y + h);
  237.             while (!draw_ok(dds->BltFast(offset_x, offset_y, scroll_src_dds, &r, DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT)));
  238.  
  239.             // Make area dirty
  240.  
  241.             add_dirty(0, start - delta, w - 1, start);
  242.         }
  243.         else
  244.         {
  245.             // Everything is dirty
  246.  
  247.             all_dirty();
  248.         }
  249.     }
  250.     else if (scroll_src_dds != dds)
  251.     {
  252.         // Copy screen to backbuffer
  253.         
  254.         CRect r(offset_x, offset_y, offset_x + w, offset_y + h);
  255.         while (!draw_ok(dds->BltFast(offset_x, offset_y, scroll_src_dds, &r, DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT)));
  256.     }
  257. }
  258.  
  259. void cSurface::clear(int color)
  260. {
  261.     // Setup effect for blit
  262.  
  263.     DDBLTFX ddbltfx;
  264.     
  265.     ddbltfx.dwSize = sizeof(ddbltfx);
  266.     ddbltfx.dwFillColor = color;
  267.     
  268.     // Clear
  269.  
  270.     CRect r(0, 0,  w, h);
  271.     while (!draw_ok(dds->Blt(&r, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &ddbltfx)));
  272. }
  273.  
  274. void cSurface::clear_dirty(int color)
  275. {    
  276.     // Set blit effects structure
  277.  
  278.     DDBLTFX ddbltfx;
  279.  
  280.     ddbltfx.dwSize = sizeof(ddbltfx);
  281.     ddbltfx.dwFillColor = color;
  282.  
  283.     // Check dirty areas and clear them
  284.  
  285.     char *d = dirty;
  286.  
  287.     for (int y = 0; y < h; y += dirty_block)
  288.         for (int x = 0; x < w; x += dirty_block, d++)
  289.             if (*d)
  290.             {
  291.                 CRect r(x, y, x + dirty_block, y + dirty_block);
  292.                 while (!draw_ok(dds->Blt(&r, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &ddbltfx)));
  293.             }
  294. }
  295.  
  296. void cSurface::write_tiled(cBMP *bmp)
  297. {
  298.     // tilestart is the start of the tile
  299.     
  300.     int tilestart = start + h - ((start + h) / bmp->h + 1) * bmp->h;
  301.  
  302.     // Get best pointer to dd surface
  303.  
  304.     LPDIRECTDRAWSURFACE4 best = bmp->bestptr(this);
  305.     
  306.     // Check dirty areas and clear them
  307.  
  308.     char *d = dirty;
  309.  
  310.     for (int y = 0; y < h; y += dirty_block)
  311.         for (int x = 0; x < w; x += dirty_block, d++)
  312.             if (*d)
  313.             {
  314.                 // Get source range in image
  315.  
  316.                 int y1 = (y - tilestart) % bmp->h, 
  317.                     y2 = (y - tilestart + dirty_block - 1) % bmp->h;
  318.                 
  319.                 // Write the background
  320.  
  321.                 CRect r(x, y1, x + dirty_block, y2 > y1? y2 + 1: bmp->h);
  322.                 while (!draw_ok(dds->BltFast(offset_x + x, offset_y + y, best, &r, DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT)));
  323.  
  324.                 // Check if there's a second part and write it
  325.  
  326.                 if (y1 > y2)
  327.                 {
  328.                     CRect r(x, 0, x + dirty_block, y2 + 1);
  329.                     while (!draw_ok(dds->BltFast(offset_x + x, offset_y + y + bmp->h - y1, best, &r, DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT)));                    
  330.                 }
  331.             }
  332. }
  333.  
  334. void cSurface::write_displayable(cDisplayable *l)
  335. {
  336.     for (; l != 0; l = (cDisplayable *)l->next)
  337.     {
  338.         // Get area occupied by this displayable
  339.  
  340.         int x1 = l->x1, y1 = l->y1, x2 = l->x2, y2 = l->y2;
  341.  
  342.         if (!convert_area(x1, y1, x2, y2))
  343.             continue;
  344.  
  345.         // Write dirty blocks, writing as much as possible in horizontal direction
  346.  
  347.         for (; y1 <= y2; y1++)
  348.         {
  349.             char *d = dirty + x1 + y1 * dirty_w;
  350.             
  351.             int y_game = start + h - 1 - y1 * dirty_block;
  352.  
  353.             for (int x = x1; x <= x2; x++, d++)
  354.                 if (*d)
  355.                 {
  356.                     // Remember first block to write
  357.  
  358.                     int x_start = x * dirty_block;
  359.  
  360.                     // Get last block to write
  361.  
  362.                     for (x++, d++; x <= x2 && *d; x++, d++);
  363.  
  364.                     // Write horizontal line of blocks
  365.  
  366.                     l->write(x_start, y_game, x * dirty_block - 1, y_game - dirty_block + 1);
  367.                 }
  368.         }
  369.     }
  370. }
  371.